
#if defined(_MSC_VER)
	#pragma warning( disable : 4786 )
#endif

#include <assert.h>
#include <set>
#include "NvTriStripObjects.h"

#include <osg/Notify>

#include <string.h>

VertexCache::VertexCache()
{
    VertexCache(16);
}


VertexCache::VertexCache(int size)
{
    numEntries = size;

    entries = osgNew int[numEntries];

    for(int i = 0; i < numEntries; i++)
        entries[i] = -1;
}


VertexCache::~VertexCache()
{
    osgDelete [] entries;
}


int VertexCache::At(int index)
{
    return entries[index];
}


void VertexCache::Set(int index, int value)
{
    entries[index] = value;
}


void VertexCache::Clear()
{
    for(int i = 0; i < numEntries; i++)
        entries[i] = -1;
}


void VertexCache::Copy(VertexCache* inVcache)
{
    for(int i = 0; i < numEntries; i++)
    {
        inVcache->Set(i, entries[i]);
    }
}


NvStripifier::NvStripifier()
{

}


NvStripifier::~NvStripifier()
{

}


///////////////////////////////////////////////////////////////////////////////////////////
// FindEdgeInfo()
//
// find the edge info for these two indices
//
NvEdgeInfo * NvStripifier::FindEdgeInfo(NvEdgeInfoVec &edgeInfos, int v0, int v1)
{

    // we can get to it through either array
    // because the edge infos have a v0 and v1
    // and there is no order except how it was
    // first created.
    NvEdgeInfo *infoIter = edgeInfos[v0];
    while (infoIter != NULL)
    {
        if (infoIter->m_v0 == v0)
        {
            if (infoIter->m_v1 == v1)
                return infoIter;
            else
                infoIter = infoIter->m_nextV0;
        }
        else
        {
            assert(infoIter->m_v1 == v0);
            if (infoIter->m_v0 == v1)
                return infoIter;
            else
                infoIter = infoIter->m_nextV1;
        }
    }
    return NULL;
}


///////////////////////////////////////////////////////////////////////////////////////////
// FindOtherFace
//
// find the other face sharing these vertices
// exactly like the edge info above
//
NvFaceInfo * NvStripifier::FindOtherFace(NvEdgeInfoVec &edgeInfos, int v0, int v1, NvFaceInfo *faceInfo)
{
    NvEdgeInfo *edgeInfo = FindEdgeInfo(edgeInfos, v0, v1);
    assert(edgeInfo != NULL);
    return (edgeInfo->m_face0 == faceInfo ? edgeInfo->m_face1 : edgeInfo->m_face0);
}


bool NvStripifier::AlreadyExists(NvFaceInfo* faceInfo, NvFaceInfoVec& faceInfos)
{
    for(unsigned int i = 0; i < faceInfos.size(); i++)
    {
        if( (faceInfos[i]->m_v0 == faceInfo->m_v0) &&
            (faceInfos[i]->m_v1 == faceInfo->m_v1) &&
            (faceInfos[i]->m_v2 == faceInfo->m_v2) )
            return true;
    }

    return false;
}


///////////////////////////////////////////////////////////////////////////////////////////
// BuildStripifyInfo()
//
// Builds the list of all face and edge infos
//
void NvStripifier::BuildStripifyInfo(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos, const int numVertices)
{

    // reserve space for the face infos, but do not resize them.
    int numIndices = indices.size();
    int numTriangles = numIndices / 3;

    faceInfos.reserve(numTriangles);

    // we actually resize the edge infos, so we must initialize to NULL
    edgeInfos.resize (numVertices);
    int i;
    for (i = 0; i < numVertices; i++)
        edgeInfos[i] = NULL;


    // iterate through the triangles of the triangle list
    int index        = 0;
    for (i = 0; i < numTriangles; i++)
    {
        // grab the indices
        int v0 = indices[index++];
        int v1 = indices[index++];
        int v2 = indices[index++];

        // create the face info and add it to the list of faces, but only if this exact face doesn't already
        //  exist in the list
        NvFaceInfo *faceInfo = osgNew NvFaceInfo(v0, v1, v2);
        if(!AlreadyExists(faceInfo, faceInfos))
        {
            faceInfos.push_back(faceInfo);

            // grab the edge infos, creating them if they do not already exist
            NvEdgeInfo *edgeInfo01 = FindEdgeInfo(edgeInfos, v0, v1);
            if (edgeInfo01 == NULL)
            {

                // create the info
                edgeInfo01 = osgNew NvEdgeInfo(v0, v1);

                // update the linked list on both
                edgeInfo01->m_nextV0 = edgeInfos[v0];
                edgeInfo01->m_nextV1 = edgeInfos[v1];
                edgeInfos[v0] = edgeInfo01;
                edgeInfos[v1] = edgeInfo01;

                // set face 0
                edgeInfo01->m_face0 = faceInfo;
            }
            else
            {
                if (edgeInfo01->m_face1 != NULL)
                    osg::notify(osg::WARN)<<"BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"<< std::endl;
                else
                    edgeInfo01->m_face1 = faceInfo;
            }

            // grab the edge infos, creating them if they do not already exist
            NvEdgeInfo *edgeInfo12 = FindEdgeInfo(edgeInfos, v1, v2);
            if (edgeInfo12 == NULL)
            {

                // create the info
                edgeInfo12 = osgNew NvEdgeInfo(v1, v2);

                // update the linked list on both
                edgeInfo12->m_nextV0 = edgeInfos[v1];
                edgeInfo12->m_nextV1 = edgeInfos[v2];
                edgeInfos[v1] = edgeInfo12;
                edgeInfos[v2] = edgeInfo12;

                // set face 0
                edgeInfo12->m_face0 = faceInfo;
            }
            else
            {
                if (edgeInfo12->m_face1 != NULL)
                    osg::notify(osg::WARN)<<"BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"<< std::endl;
                else
                    edgeInfo12->m_face1 = faceInfo;
            }

            // grab the edge infos, creating them if they do not already exist
            NvEdgeInfo *edgeInfo20 = FindEdgeInfo(edgeInfos, v2, v0);
            if (edgeInfo20 == NULL)
            {

                // create the info
                edgeInfo20 = osgNew NvEdgeInfo(v2, v0);

                // update the linked list on both
                edgeInfo20->m_nextV0 = edgeInfos[v2];
                edgeInfo20->m_nextV1 = edgeInfos[v0];
                edgeInfos[v2] = edgeInfo20;
                edgeInfos[v0] = edgeInfo20;

                // set face 0
                edgeInfo20->m_face0 = faceInfo;
            }
            else
            {
                if (edgeInfo20->m_face1 != NULL)
                    osg::notify(osg::WARN)<<"BuildStripifyInfo: > 2 triangles on an edge... uncertain consequences\n"<< std::endl;
                else
                    edgeInfo20->m_face1 = faceInfo;
            }

        }
    }
}


///////////////////////////////////////////////////////////////////////////////////////////
// FindStartPoint()
//
// Finds a good starting point, namely one which has only one neighbor
//
int NvStripifier::FindStartPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos)
{
    for(unsigned int i = 0; i < faceInfos.size(); i++)
    {
        int ctr = 0;

        if(FindOtherFace(edgeInfos, faceInfos[i]->m_v0, faceInfos[i]->m_v1, faceInfos[i]) == NULL)
            ctr++;
        if(FindOtherFace(edgeInfos, faceInfos[i]->m_v1, faceInfos[i]->m_v2, faceInfos[i]) == NULL)
            ctr++;
        if(FindOtherFace(edgeInfos, faceInfos[i]->m_v2, faceInfos[i]->m_v0, faceInfos[i]) == NULL)
            ctr++;
        if(ctr > 1)
            return i;
    }
    return -1;
}


///////////////////////////////////////////////////////////////////////////////////////////
// FindGoodResetPoint()
//
// A good reset point is one near other commited areas so that
// we know that when we've made the longest strips its because
// we're stripifying in the same general orientation.
//
NvFaceInfo* NvStripifier::FindGoodResetPoint(NvFaceInfoVec &faceInfos, NvEdgeInfoVec &edgeInfos)
{
    // we hop into different areas of the mesh to try to get
    // other large open spans done.  Areas of small strips can
    // just be left to triangle lists added at the end.
    NvFaceInfo *result = NULL;

    if(result == NULL)
    {
        int numFaces   = faceInfos.size();
        int startPoint;
        if(bFirstTimeResetPoint)
        {
            //first time, find a face with few neighbors (look for an edge of the mesh)
            startPoint = FindStartPoint(faceInfos, edgeInfos);
            bFirstTimeResetPoint = false;
        }
        else
            startPoint = (int)(((float) numFaces - 1) * meshJump);

        if(startPoint == -1)
            startPoint = (int)(((float) numFaces - 1) * meshJump);

        int i = startPoint;
        do
        {

            // if this guy isn't visited, try him
            if (faceInfos[i]->m_stripId < 0)
            {
                result = faceInfos[i];
                break;
            }

            // update the index and clamp to 0-(numFaces-1)
            if (++i >= numFaces)
                i = 0;

        } while (i != startPoint);

        // update the meshJump
        meshJump += 0.1f;
        if (meshJump > 1.0f)
            meshJump = .05f;
    }

    // return the best face we found
    return result;
}


///////////////////////////////////////////////////////////////////////////////////////////
// GetUniqueVertexInB()
//
// Returns the vertex unique to faceB
//
int NvStripifier::GetUniqueVertexInB(NvFaceInfo *faceA, NvFaceInfo *faceB)
{

    int facev0 = faceB->m_v0;
    if (facev0 != faceA->m_v0 &&
        facev0 != faceA->m_v1 &&
        facev0 != faceA->m_v2)
        return facev0;

    int facev1 = faceB->m_v1;
    if (facev1 != faceA->m_v0 &&
        facev1 != faceA->m_v1 &&
        facev1 != faceA->m_v2)
        return facev1;

    int facev2 = faceB->m_v2;
    if (facev2 != faceA->m_v0 &&
        facev2 != faceA->m_v1 &&
        facev2 != faceA->m_v2)
        return facev2;

    // nothing is different
    return -1;
}


///////////////////////////////////////////////////////////////////////////////////////////
// GetSharedVertex()
//
// Returns the vertex shared between the two input faces
//
int NvStripifier::GetSharedVertex(NvFaceInfo *faceA, NvFaceInfo *faceB)
{

    int facev0 = faceB->m_v0;
    if (facev0 == faceA->m_v0 ||
        facev0 == faceA->m_v1 ||
        facev0 == faceA->m_v2)
        return facev0;

    int facev1 = faceB->m_v1;
    if (facev1 == faceA->m_v0 ||
        facev1 == faceA->m_v1 ||
        facev1 == faceA->m_v2)
        return facev1;

    int facev2 = faceB->m_v2;
    if (facev2 == faceA->m_v0 ||
        facev2 == faceA->m_v1 ||
        facev2 == faceA->m_v2)
        return facev2;

    // nothing is shared
    return -1;
}


///////////////////////////////////////////////////////////////////////////////////////////
// GetNextIndex()
//
// Returns vertex of the input face which is "next" in the input index list
//
inline int NvStripifier::GetNextIndex(const WordVec &indices, NvFaceInfo *face)
{

    int numIndices = indices.size();
    assert(numIndices >= 2);

    int v0  = indices[numIndices-2];
    int v1  = indices[numIndices-1];

    int fv0 = face->m_v0;
    int fv1 = face->m_v1;
    int fv2 = face->m_v2;

    if (fv0 != v0 && fv0 != v1)
    {
        if ((fv1 != v0 && fv1 != v1) || (fv2 != v0 && fv2 != v1))
        {
            osg::notify(osg::WARN)<<"GetNextIndex: Triangle doesn't have all of its vertices\n"<< std::endl;
            osg::notify(osg::WARN)<<"GetNextIndex: Duplicate triangle probably got us derailed\n"<< std::endl;
        }
        return fv0;
    }
    if (fv1 != v0 && fv1 != v1)
    {
        if ((fv0 != v0 && fv0 != v1) || (fv2 != v0 && fv2 != v1))
        {
            osg::notify(osg::WARN)<<"GetNextIndex: Triangle doesn't have all of its vertices\n"<< std::endl;
            osg::notify(osg::WARN)<<"GetNextIndex: Duplicate triangle probably got us derailed\n"<< std::endl;
        }
        return fv1;
    }
    if (fv2 != v0 && fv2 != v1)
    {
        if ((fv0 != v0 && fv0 != v1) || (fv1 != v0 && fv1 != v1))
        {
            osg::notify(osg::WARN)<<"GetNextIndex: Triangle doesn't have all of its vertices\n"<< std::endl;
            osg::notify(osg::WARN)<<"GetNextIndex: Duplicate triangle probably got us derailed\n"<< std::endl;
        }
        return fv2;
    }

    // shouldn't get here
    osg::notify(osg::WARN)<<"GetNextIndex: Duplicate triangle sent\n"<< std::endl;
    return -1;
}


///////////////////////////////////////////////////////////////////////////////////////////
// IsMarked()
//
// If either the faceInfo has a real strip index because it is
// already assign to a committed strip OR it is assigned in an
// experiment and the experiment index is the one we are building
// for, then it is marked and unavailable
inline bool NvStripInfo::IsMarked(NvFaceInfo *faceInfo)
{
    return (faceInfo->m_stripId >= 0) || (IsExperiment() && faceInfo->m_experimentId == m_experimentId);
}


///////////////////////////////////////////////////////////////////////////////////////////
// MarkTriangle()
//
// Marks the face with the current strip ID
//
inline void NvStripInfo::MarkTriangle(NvFaceInfo *faceInfo)
{
    assert(!IsMarked(faceInfo));
    if (IsExperiment())
    {
        faceInfo->m_experimentId = m_experimentId;
        faceInfo->m_testStripId  = m_stripId;
    }
    else
    {
        assert(faceInfo->m_stripId == -1);
        faceInfo->m_experimentId = -1;
        faceInfo->m_stripId      = m_stripId;
    }
}


bool NvStripInfo::Unique(NvFaceInfoVec& faceVec, NvFaceInfo* face)
{
    bool bv0, bv1, bv2;          //bools to indicate whether a vertex is in the faceVec or not
    bv0 = bv1 = bv2 = false;

    for(unsigned int i = 0; i < faceVec.size(); i++)
    {
        if(!bv0)
        {
            if( (faceVec[i]->m_v0 == face->m_v0) ||
                (faceVec[i]->m_v1 == face->m_v0) ||
                (faceVec[i]->m_v2 == face->m_v0) )
                bv0 = true;
        }

        if(!bv1)
        {
            if( (faceVec[i]->m_v0 == face->m_v1) ||
                (faceVec[i]->m_v1 == face->m_v1) ||
                (faceVec[i]->m_v2 == face->m_v1) )
                bv1 = true;
        }

        if(!bv2)
        {
            if( (faceVec[i]->m_v0 == face->m_v2) ||
                (faceVec[i]->m_v1 == face->m_v2) ||
                (faceVec[i]->m_v2 == face->m_v2) )
                bv2 = true;
        }

        //the face is not unique, all it's vertices exist in the face vector
        if(bv0 && bv1 && bv2)
            return false;
    }

    //if we get out here, it's unique
    return true;
}


///////////////////////////////////////////////////////////////////////////////////////////
// Build()
//
// Builds a strip forward as far as we can go, then builds backwards, and joins the two lists
//
void NvStripInfo::Build(NvEdgeInfoVec &edgeInfos, NvFaceInfoVec &)
{
    // parameters : void NvStripInfo::Build(NvEdgeInfoVec &edgeInfos, NvFaceInfoVec &faceInfos)

    // used in building the strips forward and backward
    static WordVec scratchIndices;
    scratchIndices.resize(0);

    // build forward... start with the initial face
    NvFaceInfoVec forwardFaces, backwardFaces;
    forwardFaces.push_back(m_startInfo.m_startFace);

    MarkTriangle(m_startInfo.m_startFace);

    int v0 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge->m_v0 : m_startInfo.m_startEdge->m_v1);
    int v1 = (m_startInfo.m_toV1 ? m_startInfo.m_startEdge->m_v1 : m_startInfo.m_startEdge->m_v0);

    // easiest way to get v2 is to use this function which requires the
    // other indices to already be in the list.
    scratchIndices.push_back(v0);
    scratchIndices.push_back(v1);
    int v2 = NvStripifier::GetNextIndex(scratchIndices, m_startInfo.m_startFace);
    scratchIndices.push_back(v2);

    //
    // build the forward list
    //
    int nv0 = v1;
    int nv1 = v2;

    NvFaceInfo *nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
    while (nextFace != NULL && !IsMarked(nextFace))
    {
        //this tests to see if a face is "unique", meaning that its vertices aren't already in the list
        // so, strips which "wrap-around" are not allowed
        if(!Unique(forwardFaces, nextFace))
            break;

        // add this to the strip
        forwardFaces.push_back(nextFace);

        MarkTriangle(nextFace);

        // add the index
        nv0 = nv1;
        nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
        scratchIndices.push_back(nv1);

        // and get the next face
        nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, nextFace);

    }

    // tempAllFaces is going to be forwardFaces + backwardFaces
    // it's used for Unique()
    NvFaceInfoVec tempAllFaces;
    for(unsigned int i = 0; i < forwardFaces.size(); i++)
        tempAllFaces.push_back(forwardFaces[i]);

    //
    // reset the indices for building the strip backwards and do so
    //
    scratchIndices.resize(0);
    scratchIndices.push_back(v2);
    scratchIndices.push_back(v1);
    scratchIndices.push_back(v0);
    nv0 = v1;
    nv1 = v0;
    nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, m_startInfo.m_startFace);
    while (nextFace != NULL && !IsMarked(nextFace))
    {
        //this tests to see if a face is "unique", meaning that its vertices aren't already in the list
        // so, strips which "wrap-around" are not allowed
        if(!Unique(tempAllFaces, nextFace))
            break;

        // add this to the strip
        backwardFaces.push_back(nextFace);

        //this is just so Unique() will work
        tempAllFaces.push_back(nextFace);

        MarkTriangle(nextFace);

        // add the index
        nv0 = nv1;
        nv1 = NvStripifier::GetNextIndex(scratchIndices, nextFace);
        scratchIndices.push_back(nv1);

        // and get the next face
        nextFace = NvStripifier::FindOtherFace(edgeInfos, nv0, nv1, nextFace);
    }

    // Combine the forward and backwards stripification lists and put into our own face vector
    Combine(forwardFaces, backwardFaces);
}


///////////////////////////////////////////////////////////////////////////////////////////
// Combine()
//
// Combines the two input face vectors and puts the result into m_faces
//
void NvStripInfo::Combine(const NvFaceInfoVec &forward, const NvFaceInfoVec &backward)
{

    // add backward faces
    int numFaces = backward.size();
    int i;
    for (i = numFaces - 1; i >= 0; i--)
        m_faces.push_back(backward[i]);

    // add forward faces
    numFaces = forward.size();
    for (i = 0; i < numFaces; i++)
        m_faces.push_back(forward[i]);
}


///////////////////////////////////////////////////////////////////////////////////////////
// SharesEdge()
//
// Returns true if the input face and the current strip share an edge
//
bool NvStripInfo::SharesEdge(const NvFaceInfo* faceInfo, NvEdgeInfoVec &edgeInfos)
{
    //check v0->v1 edge
    NvEdgeInfo* currEdge = NvStripifier::FindEdgeInfo(edgeInfos, faceInfo->m_v0, faceInfo->m_v1);

    if(IsInStrip(currEdge->m_face0) || IsInStrip(currEdge->m_face1))
        return true;

    //check v1->v2 edge
    currEdge = NvStripifier::FindEdgeInfo(edgeInfos, faceInfo->m_v1, faceInfo->m_v2);

    if(IsInStrip(currEdge->m_face0) || IsInStrip(currEdge->m_face1))
        return true;

    //check v2->v0 edge
    currEdge = NvStripifier::FindEdgeInfo(edgeInfos, faceInfo->m_v2, faceInfo->m_v0);

    if(IsInStrip(currEdge->m_face0) || IsInStrip(currEdge->m_face1))
        return true;

    return false;

}


///////////////////////////////////////////////////////////////////////////////////////////
// CommitStrips()
//
// "Commits" the input strips by setting their m_experimentId to -1 and adding to the allStrips
//  vector
//
void NvStripifier::CommitStrips(NvStripInfoVec &allStrips, const NvStripInfoVec &strips)
{
    // Iterate through strips
    int numStrips = strips.size();
    for (int i = 0; i < numStrips; i++)
    {

        // Tell the strip that it is now real
        NvStripInfo *strip = strips[i];
        strip->m_experimentId = -1;

        // add to the list of real strips
        allStrips.push_back(strip);

        // Iterate through the faces of the strip
        // Tell the faces of the strip that they belong to a real strip now
        const NvFaceInfoVec &faces = strips[i]->m_faces;
        int numFaces = faces.size();

        if( (faces[0]->m_v0 == 2302) &&
            (faces[0]->m_v1 == 3215) &&
            (faces[0]->m_v2 == 2603) )
            osg::notify(osg::WARN)<<"BLEH"<< std::endl;

        for (int j = 0; j < numFaces; j++)
        {
            strip->MarkTriangle(faces[j]);
        }
    }
}


///////////////////////////////////////////////////////////////////////////////////////////
// FindTraversal()
//
// Finds the next face to start the next strip on.
//
bool NvStripifier::FindTraversal(NvFaceInfoVec &,
NvEdgeInfoVec    &edgeInfos,
NvStripInfo      *strip,
NvStripStartInfo &startInfo)
{

    // if the strip was v0->v1 on the edge, then v1 will be a vertex in the next edge.
    int v = (strip->m_startInfo.m_toV1 ? strip->m_startInfo.m_startEdge->m_v1 : strip->m_startInfo.m_startEdge->m_v0);

    NvFaceInfo *untouchedFace = NULL;
    NvEdgeInfo *edgeIter      = edgeInfos[v];
    while (edgeIter != NULL)
    {
        NvFaceInfo *face0 = edgeIter->m_face0;
        NvFaceInfo *face1 = edgeIter->m_face1;
        if ((face0 != NULL && !strip->IsInStrip(face0)) && face1 != NULL && !strip->IsMarked(face1))
        {
            untouchedFace = face1;
            break;
        }
        if ((face1 != NULL && !strip->IsInStrip(face1)) && face0 != NULL && !strip->IsMarked(face0))
        {
            untouchedFace = face0;
            break;
        }

        // find the next edgeIter
        edgeIter = (edgeIter->m_v0 == v ? edgeIter->m_nextV0 : edgeIter->m_nextV1);
    }

    startInfo.m_startFace = untouchedFace;
    startInfo.m_startEdge = edgeIter;
    if (edgeIter != NULL)
    {
        if(strip->SharesEdge(startInfo.m_startFace, edgeInfos))
                                 //note! used to be m_v1
            startInfo.m_toV1 = (edgeIter->m_v0 == v);
        else
            startInfo.m_toV1 = (edgeIter->m_v1 == v);
    }
    return (startInfo.m_startFace != NULL);
}


////////////////////////////////////////////////////////////////////////////////////////
// RemoveSmallStrips()
//
// allStrips is the whole strip vector...all small strips will be deleted from this list, to avoid leaking mem
// allBigStrips is an out parameter which will contain all strips above minStripLength
// faceList is an out parameter which will contain all faces which were removed from the striplist
//
void NvStripifier::RemoveSmallStrips(NvStripInfoVec& allStrips, NvStripInfoVec& allBigStrips, NvFaceInfoVec& faceList)
{
    faceList.clear();
    allBigStrips.clear();        //make sure these are empty
    NvFaceInfoVec tempFaceList;

    for(unsigned int i = 0; i < allStrips.size(); i++)
    {
        if(allStrips[i]->m_faces.size() < minStripLength)
        {
            //strip is too small, add faces to faceList
            for(unsigned int j = 0; j < allStrips[i]->m_faces.size(); j++)
                tempFaceList.push_back(allStrips[i]->m_faces[j]);

            //and free memory
            osgDelete allStrips[i];
        }
        else
        {
            allBigStrips.push_back(allStrips[i]);
        }
    }

    bool *bVisitedList = osgNew bool[tempFaceList.size()];
    memset(bVisitedList, 0, tempFaceList.size()*sizeof(bool));

    VertexCache* vcache = osgNew VertexCache(cacheSize);

    int bestNumHits = -1;
    int numHits;
    int bestIndex = 0;

    while(1)
    {
        bestNumHits = -1;

        //find best face to add next, given the current cache
        for(unsigned int i = 0; i < tempFaceList.size(); i++)
        {
            if(bVisitedList[i])
                continue;

            numHits = CalcNumHitsFace(vcache, tempFaceList[i]);
            if(numHits > bestNumHits)
            {
                bestNumHits = numHits;
                bestIndex = i;
            }
        }

        if(bestNumHits == -1.0)
            break;
        bVisitedList[bestIndex] = true;
        UpdateCacheFace(vcache, tempFaceList[bestIndex]);
        faceList.push_back(tempFaceList[bestIndex]);
    }

    osgDelete vcache;
    osgDelete [] bVisitedList;
}


///////////////////////////////////////////////////////////////////////////////////////////
// Stripify()
//
//
// in_indices are the input indices of the mesh to stripify
// in_cacheSize is the target cache size
//
void NvStripifier::Stripify(const WordVec &in_indices, const int in_numVertices, const int in_cacheSize,
    const int in_minStripLength, NvStripInfoVec &outStrips, NvFaceInfoVec& outFaceList)
{
    meshJump = 0.0f;
    bFirstTimeResetPoint = true; //used in FindGoodResetPoint()

    //the number of times to run the experiments
    int numSamples = 10;
    cacheSize = in_cacheSize;
                                 //this is the strip size threshold below which we dump the strip into a list
    minStripLength = in_minStripLength;

    indices = in_indices;

    // build the stripification info
    NvFaceInfoVec allFaceInfos;
    NvEdgeInfoVec allEdgeInfos;

    BuildStripifyInfo(allFaceInfos, allEdgeInfos, in_numVertices);

    NvStripInfoVec allStrips;

    // stripify
    FindAllStrips(allStrips, allFaceInfos, allEdgeInfos, numSamples);

    //split up the strips into cache friendly pieces, optimize them, then dump these into outStrips
    SplitUpStripsAndOptimize(allStrips, outStrips, allEdgeInfos, outFaceList);

    //clean up
    int i;
    for(i = 0; i < (int)allStrips.size(); i++)
    {
        osgDelete allStrips[i];
    }

    for (i = 0; i < (int)allEdgeInfos.size(); i++)
    {
        NvEdgeInfo *info = allEdgeInfos[i];
        while (info != NULL)
        {
            NvEdgeInfo *next = (info->m_v0 == i ? info->m_nextV0 : info->m_nextV1);
            info->Unref();
            info = next;
        }
    }

}


///////////////////////////////////////////////////////////////////////////////////////////
// SplitUpStripsAndOptimize()
//
// Splits the input vector of strips (allBigStrips) into smaller, cache friendly pieces, then
//  reorders these pieces to maximize cache hits
// The final strips are output through outStrips
//
void NvStripifier::SplitUpStripsAndOptimize(NvStripInfoVec &allStrips, NvStripInfoVec &outStrips,
NvEdgeInfoVec& edgeInfos, NvFaceInfoVec& outFaceList)
{
    int threshold = cacheSize - 4;
    NvStripInfoVec tempStrips;

    //split up strips into threshold-sized pieces
    unsigned int i;
    for(i = 0; i < allStrips.size(); i++)
    {
        NvStripInfo* currentStrip;
        NvStripStartInfo startInfo(NULL, NULL, false);

        if((int)(allStrips[i]->m_faces.size()) > threshold)
        {

            int numTimes    = allStrips[i]->m_faces.size() / threshold;
            int numLeftover = allStrips[i]->m_faces.size() % threshold;

            int j;
            for(j = 0; j < numTimes; j++)
            {
                currentStrip = osgNew NvStripInfo(startInfo, 0, -1);

                for(int faceCtr = j*threshold; faceCtr < threshold+(j*threshold); faceCtr++)
                {
                    currentStrip->m_faces.push_back(allStrips[i]->m_faces[faceCtr]);
                }

                tempStrips.push_back(currentStrip);
            }

            int leftOff = j * threshold;

            if(numLeftover != 0)
            {
                currentStrip = osgNew NvStripInfo(startInfo, 0, -1);

                for(int k = 0; k < numLeftover; k++)
                {
                    currentStrip->m_faces.push_back(allStrips[i]->m_faces[leftOff++]);
                }

                tempStrips.push_back(currentStrip);
            }
        }
        else
        {
            //we're not just doing a tempStrips.push_back(allBigStrips[i]) because
            // this way we can osgDelete allBigStrips later to free the memory
            currentStrip = osgNew NvStripInfo(startInfo, 0, -1);

            for(unsigned int j = 0; j < allStrips[i]->m_faces.size(); j++)
                currentStrip->m_faces.push_back(allStrips[i]->m_faces[j]);

            tempStrips.push_back(currentStrip);
        }
    }

    //add small strips to face list
    NvStripInfoVec tempStrips2;
    RemoveSmallStrips(tempStrips, tempStrips2, outFaceList);

    outStrips.clear();
    if(tempStrips2.size() != 0)
    {
        //Optimize for the vertex cache
        VertexCache* vcache = osgNew VertexCache(cacheSize);

        float bestNumHits = -1.0f;
        float numHits;
        int bestIndex=0;

        int firstIndex = 0;
        float minCost = 10000.0f;

        for(i = 0; i < tempStrips2.size(); i++)
        {
            int numNeighbors = 0;

            //find strip with least number of neighbors per face
            for(unsigned int j = 0; j < tempStrips2[i]->m_faces.size(); j++)
            {
                numNeighbors += NumNeighbors(tempStrips2[i]->m_faces[j], edgeInfos);
            }

            float currCost = (float)numNeighbors / (float)tempStrips2[i]->m_faces.size();
            if(currCost < minCost)
            {
                minCost = currCost;
                firstIndex = i;
            }
        }

        UpdateCacheStrip(vcache, tempStrips2[firstIndex]);
        outStrips.push_back(tempStrips2[firstIndex]);

        tempStrips2[firstIndex]->visited = true;

        //this n^2 algo is what slows down stripification so much....
        // needs to be improved
        while(1)
        {
            bestNumHits = -1.0f;

            //find best strip to add next, given the current cache
            for(unsigned int i = 0; i < tempStrips2.size(); i++)
            {
                if(tempStrips2[i]->visited)
                    continue;

                numHits = CalcNumHitsStrip(vcache, tempStrips2[i]);
                if(numHits > bestNumHits)
                {
                    bestNumHits = numHits;
                    bestIndex = i;
                }
            }

            if(bestNumHits == -1.0f)
                break;
            tempStrips2[bestIndex]->visited = true;
            UpdateCacheStrip(vcache, tempStrips2[bestIndex]);
            outStrips.push_back(tempStrips2[bestIndex]);
        }

        osgDelete vcache;
    }
}


///////////////////////////////////////////////////////////////////////////////////////////
// UpdateCacheStrip()
//
// Updates the input vertex cache with this strip's vertices
//
void NvStripifier::UpdateCacheStrip(VertexCache* vcache, NvStripInfo* strip)
{
    for(unsigned int i = 0; i < strip->m_faces.size(); i++)
    {
        if(!vcache->InCache(strip->m_faces[i]->m_v0))
            vcache->AddEntry(strip->m_faces[i]->m_v0);

        if(!vcache->InCache(strip->m_faces[i]->m_v1))
            vcache->AddEntry(strip->m_faces[i]->m_v1);

        if(!vcache->InCache(strip->m_faces[i]->m_v2))
            vcache->AddEntry(strip->m_faces[i]->m_v2);
    }
}


///////////////////////////////////////////////////////////////////////////////////////////
// UpdateCacheFace()
//
// Updates the input vertex cache with this face's vertices
//
void NvStripifier::UpdateCacheFace(VertexCache* vcache, NvFaceInfo* face)
{
    if(!vcache->InCache(face->m_v0))
        vcache->AddEntry(face->m_v0);

    if(!vcache->InCache(face->m_v1))
        vcache->AddEntry(face->m_v1);

    if(!vcache->InCache(face->m_v2))
        vcache->AddEntry(face->m_v2);
}


///////////////////////////////////////////////////////////////////////////////////////////
// CalcNumHitsStrip()
//
// returns the number of cache hits per face in the strip
//
float NvStripifier::CalcNumHitsStrip(VertexCache* vcache, NvStripInfo* strip)
{
    int numHits = 0;
    int numFaces = 0;

    for(unsigned int i = 0; i < strip->m_faces.size(); i++)
    {
        if(vcache->InCache(strip->m_faces[i]->m_v0))
            numHits++;

        if(vcache->InCache(strip->m_faces[i]->m_v1))
            numHits++;

        if(vcache->InCache(strip->m_faces[i]->m_v2))
            numHits++;

        numFaces++;

    }

    return ((float)numHits / (float)numFaces);
}


///////////////////////////////////////////////////////////////////////////////////////////
// CalcNumHitsFace()
//
// returns the number of cache hits in the face
//
int NvStripifier::CalcNumHitsFace(VertexCache* vcache, NvFaceInfo* face)
{
    int numHits = 0;

    if(vcache->InCache(face->m_v0))
        numHits++;

    if(vcache->InCache(face->m_v1))
        numHits++;

    if(vcache->InCache(face->m_v2))
        numHits++;

    return numHits;
}


///////////////////////////////////////////////////////////////////////////////////////////
// NumNeighbors()
//
// Returns the number of neighbors that this face has
//
int NvStripifier::NumNeighbors(NvFaceInfo* face, NvEdgeInfoVec& edgeInfoVec)
{
    int numNeighbors = 0;

    if(FindOtherFace(edgeInfoVec, face->m_v0, face->m_v1, face) != NULL)
    {
        numNeighbors++;
    }

    if(FindOtherFace(edgeInfoVec, face->m_v1, face->m_v2, face) != NULL)
    {
        numNeighbors++;
    }

    if(FindOtherFace(edgeInfoVec, face->m_v2, face->m_v0, face) != NULL)
    {
        numNeighbors++;
    }

    return numNeighbors;
}


///////////////////////////////////////////////////////////////////////////////////////////
// AvgStripSize()
//
// Finds the average strip size of the input vector of strips
//
float NvStripifier::AvgStripSize(const NvStripInfoVec &strips)
{
    int sizeAccum = 0;
    int numStrips = strips.size();
    for (int i = 0; i < numStrips; i++)
    {
        NvStripInfo *strip = strips[i];
        sizeAccum += strip->m_faces.size();
    }
    return ((float)sizeAccum) / ((float)numStrips);
}


///////////////////////////////////////////////////////////////////////////////////////////
// FindAllStrips()
//
// Does the stripification, puts output strips into vector allStrips
//
// Works by setting runnning a number of experiments in different areas of the mesh, and
//  accepting the one which results in the longest strips.  It then accepts this, and moves
//  on to a different area of the mesh.  We try to jump around the mesh some, to ensure that
//  large open spans of strips get generated.
//
void NvStripifier::FindAllStrips(NvStripInfoVec &allStrips,
NvFaceInfoVec &allFaceInfos,
NvEdgeInfoVec &allEdgeInfos,
int numSamples)
{
    // the experiments
    int experimentId = 0;
    int stripId      = 0;
    bool done        = false;

    int loopCtr = 0;

    while (!done)
    {
        loopCtr++;

        //
        // PHASE 1: Set up numSamples * numEdges experiments
        //
        NvStripInfoVec *experiments = osgNew NvStripInfoVec [numSamples * 6];
        int experimentIndex = 0;
        std::set   <NvFaceInfo*>  resetPoints;
        int i;
        for (i = 0; i < numSamples; i++)
        {

            // Try to find another good reset point.
            // If there are none to be found, we are done
            NvFaceInfo *nextFace = FindGoodResetPoint(allFaceInfos, allEdgeInfos);
            if (nextFace == NULL)
            {
                done = true;
                break;
            }

            // If we have already evaluated starting at this face in this slew
            // of experiments, then skip going any further
            else if (resetPoints.find(nextFace) != resetPoints.end())
            {
                continue;
            }

            // trying it now...
            resetPoints.insert(nextFace);

            // otherwise, we shall now try experiments for starting on the 01,12, and 20 edges
            assert(nextFace->m_stripId < 0);

            // build the strip off of this face's 0-1 edge
            NvEdgeInfo *edge01 = FindEdgeInfo(allEdgeInfos, nextFace->m_v0, nextFace->m_v1);
            NvStripInfo *strip01 = osgNew NvStripInfo(NvStripStartInfo(nextFace, edge01, true), stripId++, experimentId++);
            experiments[experimentIndex++].push_back(strip01);

            // build the strip off of this face's 1-0 edge
            NvEdgeInfo *edge10 = FindEdgeInfo(allEdgeInfos, nextFace->m_v0, nextFace->m_v1);
            NvStripInfo *strip10 = osgNew NvStripInfo(NvStripStartInfo(nextFace, edge10, false), stripId++, experimentId++);
            experiments[experimentIndex++].push_back(strip10);

            // build the strip off of this face's 1-2 edge
            NvEdgeInfo *edge12 = FindEdgeInfo(allEdgeInfos, nextFace->m_v1, nextFace->m_v2);
            NvStripInfo *strip12 = osgNew NvStripInfo(NvStripStartInfo(nextFace, edge12, true), stripId++, experimentId++);
            experiments[experimentIndex++].push_back(strip12);

            // build the strip off of this face's 2-1 edge
            NvEdgeInfo *edge21 = FindEdgeInfo(allEdgeInfos, nextFace->m_v1, nextFace->m_v2);
            NvStripInfo *strip21 = osgNew NvStripInfo(NvStripStartInfo(nextFace, edge21, false), stripId++, experimentId++);
            experiments[experimentIndex++].push_back(strip21);

            // build the strip off of this face's 2-0 edge
            NvEdgeInfo *edge20 = FindEdgeInfo(allEdgeInfos, nextFace->m_v2, nextFace->m_v0);
            NvStripInfo *strip20 = osgNew NvStripInfo(NvStripStartInfo(nextFace, edge20, true), stripId++, experimentId++);
            experiments[experimentIndex++].push_back(strip20);

            // build the strip off of this face's 0-2 edge
            NvEdgeInfo *edge02 = FindEdgeInfo(allEdgeInfos, nextFace->m_v2, nextFace->m_v0);
            NvStripInfo *strip02 = osgNew NvStripInfo(NvStripStartInfo(nextFace, edge02, false), stripId++, experimentId++);
            experiments[experimentIndex++].push_back(strip02);
        }

        //
        // PHASE 2: Iterate through that we setup in the last phase
        // and really build each of the strips and strips that follow to see how
        // far we get
        //
        int numExperiments = experimentIndex;
        for (i = 0; i < numExperiments; i++)
        {

            // get the strip set

            // build the first strip of the list
            experiments[i][0]->Build(allEdgeInfos, allFaceInfos);
            int experimentId = experiments[i][0]->m_experimentId;

            NvStripInfo *stripIter = experiments[i][0];
            NvStripStartInfo startInfo(NULL, NULL, false);
            while (FindTraversal(allFaceInfos, allEdgeInfos, stripIter, startInfo))
            {

                // create the new strip info
                stripIter = osgNew NvStripInfo(startInfo, stripId++, experimentId);

                // build the next strip
                stripIter->Build(allEdgeInfos, allFaceInfos);

                // add it to the list
                experiments[i].push_back(stripIter);
            }
        }

        //
        // Phase 3: Find the experiment that has the most promise
        //
        int bestIndex = 0;
        double bestValue = 0;
        for (i = 0; i < numExperiments; i++)
        {
            const float avgStripSizeWeight = 1.0f;
            const float numTrisWeight      = 1.0f;
            float avgStripSize = AvgStripSize(experiments[i]);
            float numStrips    = (float) experiments[i].size();
            float value        = avgStripSize * avgStripSizeWeight + (avgStripSize * numStrips * numTrisWeight);

            if (value > bestValue)
            {
                bestValue = value;
                bestIndex = i;
            }
        }

        //
        // Phase 4: commit the best experiment of the bunch
        //
        CommitStrips(allStrips, experiments[bestIndex]);

        // and destroy all of the others
        for (i = 0; i < numExperiments; i++)
        {
            if (i != bestIndex)
            {
                int numStrips = experiments[i].size();
                for (int j = 0; j < numStrips; j++)
                {
                    osgDelete experiments[i][j];
                }
            }
        }

        // osgDelete the array that we used for all experiments
        osgDelete [] experiments;
    }
}


///////////////////////////////////////////////////////////////////////////////////////////
// CountRemainingTris()
//
// This will count the number of triangles left in the
// strip list starting at iter and finishing up at end
//
int NvStripifier::CountRemainingTris(std::list<NvStripInfo*>::iterator iter,
std::list<NvStripInfo*>::iterator  end)
{
    int count = 0;
    while (iter != end)
    {
        count += (*iter)->m_faces.size();
        iter++;
    }
    return count;
}


///////////////////////////////////////////////////////////////////////////////////////////
// NextIsCW()
//
// Returns true if the next face should be ordered in CW fashion
//
bool NvStripifier::NextIsCW(const int numIndices)
{
    return ((numIndices % 2) == 0);
}


///////////////////////////////////////////////////////////////////////////////////////////
// IsCW()
//
// Returns true if the face is ordered in CW fashion
//
bool NvStripifier::IsCW(NvFaceInfo *faceInfo, int v0, int v1)
{
    if (faceInfo->m_v0 == v0)
        return (faceInfo->m_v1 == v1);

    else if (faceInfo->m_v1 == v0)
        return (faceInfo->m_v2 == v1);

    else
        return (faceInfo->m_v0 == v1);

    // shouldn't get here
    assert(0);
    return false;
}


//used in CreateStrips
template<class T>
inline void SWAP(T& first, T& second)
{
    T temp = first;
    first = second;
    second = temp;
}


///////////////////////////////////////////////////////////////////////////////////////////
// CreateStrips()
//
// Up until now, the strips had been strips at heart, but tri lists in reality.
// Now, remove redundant indices, and stitch together strips to form one, huge uber-strip
//  using degenerate tris.
//
void NvStripifier::CreateStrips(
NvStripInfoVec& strips,
NvFaceInfoVec&,
WordVec& stripIndices)
{
    // parameters:
    // void NvStripifier::CreateStrips(
    // NvStripInfoVec& strips,
    // NvFaceInfoVec& leftoverFaces,
    // WordVec& stripIndices)
    // {

    NvFaceInfo tLastFace(0, 0, 0);
    int nStripCount = strips.size();
    assert(nStripCount > 0);

    for (int i = 0; i < nStripCount; i++)
    {
        NvStripInfo *strip = strips[i];
        unsigned int nStripFaceCount = strip->m_faces.size();
        // unsigned int stripIndicesSize = stripIndices.size();
        assert(nStripFaceCount > 0);

        // Handle the first face in the strip
        {
            NvFaceInfo tFirstFace(strip->m_faces[0]->m_v0, strip->m_faces[0]->m_v1, strip->m_faces[0]->m_v2);

            // If there is a second face, reorder vertices such that the
            // unique vertex is first
            if (nStripFaceCount > 1)
            {
                int nUnique = NvStripifier::GetUniqueVertexInB(strip->m_faces[1], &tFirstFace);
                if (nUnique == tFirstFace.m_v1)
                {
                    SWAP(tFirstFace.m_v0, tFirstFace.m_v1);
                }
                else if (nUnique == tFirstFace.m_v2)
                {
                    SWAP(tFirstFace.m_v0, tFirstFace.m_v2);
                }

                // If there is a third face, reorder vertices such that the
                // shared vertex is last
                if (nStripFaceCount > 2)
                {
                    int nShared = NvStripifier::GetSharedVertex(strip->m_faces[2], &tFirstFace);
                    if (nShared == tFirstFace.m_v1)
                    {
                        SWAP(tFirstFace.m_v1, tFirstFace.m_v2);
                    }
                }
            }

            if (i != 0)
            {
                // Double tap the first in the new strip
                stripIndices.push_back(tFirstFace.m_v0);

                // Check CW/CCW ordering
                if (NextIsCW(stripIndices.size()) != IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1))
                {
                    stripIndices.push_back(tFirstFace.m_v0);
                }
            }
            else
            {
                if(!IsCW(strip->m_faces[0], tFirstFace.m_v0, tFirstFace.m_v1))
                    stripIndices.push_back(tFirstFace.m_v0);
            }

            stripIndices.push_back(tFirstFace.m_v0);
            stripIndices.push_back(tFirstFace.m_v1);
            stripIndices.push_back(tFirstFace.m_v2);

            // Update last face info
            tLastFace = tFirstFace;
        }

        for (unsigned int j = 1; j < nStripFaceCount; j++)
        {
            int nUnique = NvStripifier::GetUniqueVertexInB(&tLastFace, strip->m_faces[j]);
            if (nUnique != -1)
            {
                stripIndices.push_back(nUnique);

                // Update last face info
                tLastFace.m_v0 = tLastFace.m_v1;
                tLastFace.m_v1 = tLastFace.m_v2;
                tLastFace.m_v2 = nUnique;
            }
        }

        // Double tap between strips.
        stripIndices.push_back(tLastFace.m_v2);

        // Update last face info
        tLastFace.m_v0 = tLastFace.m_v1;
        tLastFace.m_v1 = tLastFace.m_v2;
        tLastFace.m_v2 = tLastFace.m_v2;
    }
}


///////////////////////////////////////////////////////////////////////////////////////////
// OptimizeVertices()
//
// Function which optimizes the vertices in the mesh to minimize page misses
//
// Puts output verts into optimizedVerts vector
//
//
void NvStripifier::OptimizeVertices(NvStripInfoVec& strips,
NvFaceInfoVec& leftoverFaces,
WordVec& ,
MyVertexVec& vertices,
MyVertexVec& optimizedVerts)
{
    // parameters.
    // void NvStripifier::OptimizeVertices(NvStripInfoVec& strips,
    // NvFaceInfoVec& leftoverFaces,
    // WordVec& stripIndices,
    // MyVertexVec& vertices,
    // MyVertexVec& optimizedVerts)

    //caches oldIndex --> newIndex conversion
    int *indexCache;
    indexCache = osgNew int[vertices.size()];

    memset(indexCache, -1, sizeof(int)*vertices.size());

    //first do the strips
    unsigned int i;
    for(i = 0; i < strips.size(); i++)
    {
        for(unsigned int j = 0; j < strips[i]->m_faces.size(); j++)
        {
            int v0 = strips[i]->m_faces[j]->m_v0;
            int v1 = strips[i]->m_faces[j]->m_v1;
            int v2 = strips[i]->m_faces[j]->m_v2;

            //v0
            int index = indexCache[v0];
            if(index == -1)
            {
                optimizedVerts.push_back(vertices[v0]);
                strips[i]->m_faces[j]->m_v0 = optimizedVerts.size() - 1;

                indexCache[v0] = strips[i]->m_faces[j]->m_v0;
            }
            else
            {
                strips[i]->m_faces[j]->m_v0 = index;
            }

            //v1
            index = indexCache[v1];
            if(index == -1)
            {
                optimizedVerts.push_back(vertices[v1]);
                strips[i]->m_faces[j]->m_v1 = optimizedVerts.size() - 1;

                indexCache[v1] = strips[i]->m_faces[j]->m_v1;
            }
            else
            {
                strips[i]->m_faces[j]->m_v1 = index;
            }

            //v2
            index = indexCache[v2];
            if(index == -1)
            {
                optimizedVerts.push_back(vertices[v2]);
                strips[i]->m_faces[j]->m_v2 = optimizedVerts.size() - 1;

                indexCache[v2] = strips[i]->m_faces[j]->m_v2;
            }
            else
            {
                strips[i]->m_faces[j]->m_v2 = index;
            }

        }
    }

    //now do the leftover list
    for(i = 0; i < leftoverFaces.size(); i++)
    {
        int v0 = leftoverFaces[i]->m_v0;
        int v1 = leftoverFaces[i]->m_v1;
        int v2 = leftoverFaces[i]->m_v2;

        //v0
        int index = indexCache[v0];
        if(index == -1)
        {
            optimizedVerts.push_back(vertices[v0]);
            leftoverFaces[i]->m_v0 = optimizedVerts.size() - 1;

            indexCache[v0] = leftoverFaces[i]->m_v0;
        }
        else
        {
            leftoverFaces[i]->m_v0 = index;
        }

        //v1
        index = indexCache[v1];
        if(index == -1)
        {
            optimizedVerts.push_back(vertices[v1]);
            leftoverFaces[i]->m_v1 = optimizedVerts.size() - 1;

            indexCache[v1] = leftoverFaces[i]->m_v1;
        }
        else
        {
            leftoverFaces[i]->m_v1 = index;
        }

        //v2
        index = indexCache[v2];
        if(index == -1)
        {
            optimizedVerts.push_back(vertices[v2]);
            leftoverFaces[i]->m_v2 = optimizedVerts.size() - 1;

            indexCache[v2] = leftoverFaces[i]->m_v2;
        }
        else
        {
            leftoverFaces[i]->m_v2 = index;
        }
    }

    osgDelete [] indexCache;

    assert(optimizedVerts.size() == vertices.size());
}
